package com.hirsi.core.thread;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.hirsi.core.util.BaiduDiskUtil;
import com.hirsi.core.util.DbUtil;
import io.vertx.core.json.JsonArray;

import java.io.*;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

public class UploadThread implements Runnable{

    public static final String TARGET_PATH = "/skyeysnow-netdisk/";

    private Integer taskId;

    private String path;

    private String remotePath;

    private Integer id;

    public UploadThread(Integer taskId, String path) {
        this.taskId = taskId;
        this.path = path;
        this.remotePath = path.replace("/tmp", "");
        System.out.println(String.format("-------------创建上传，taskId=%s，path=%s，remotePath=%s。--------------", taskId, path, remotePath));
        //创建上传
        try {
            PreparedStatement ps = DbUtil.connection.prepareStatement("insert into sn_upload (task_id, path, target_path, remote_path, state, progress, split_count) " +
                    "values (?, ?, ?, ?, ?, ?, ?)");
            ps.setInt(1, taskId);
            ps.setString(2, path);
            ps.setString(3, TARGET_PATH);
            ps.setString(4, remotePath);
            ps.setInt(5, 0);
            ps.setInt(6, 0);
            ps.setInt(7, 0);
            ps.execute();
            ResultSet gk = ps.getGeneratedKeys();
            if (gk.next()) id = gk.getInt(1);
            System.out.println(String.format("-------------创建上传成功，id=%s。--------------", id));
        } catch (SQLException e) {
            System.err.println("-------------创建上传失败！--------------");
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        System.out.println(String.format("-------------开始执行上传（%s）。--------------", path));
        //执行上传
        File file = new File(path);
        //预上传
        JSONObject jsonObject = BaiduDiskUtil.preCreate(remotePath, TARGET_PATH, file.length());
        System.out.println("预上传返回：" + jsonObject);
        if (jsonObject.getInt("errno") != 0) {
            try {
                PreparedStatement ps = DbUtil.connection.prepareStatement("update sn_upload set err_msg = ? where id = ?");
                ps.setString(1, jsonObject.toString());
                ps.setInt(2, id);
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return;
        }
        String uploadid = jsonObject.getStr("uploadid");
        //秒传
        boolean rapid = false;
        if (file.length() > BaiduDiskUtil.FILE_RAPID_MIN_SIZE) {
            //2.大于262144就秒传，秒传成功则不进行后续步骤(秒传测试成功)
            jsonObject = BaiduDiskUtil.rapidUpload(uploadid, remotePath, file, TARGET_PATH);
            System.out.println("文件秒传返回：" + jsonObject);
            if (jsonObject.getInt("errno") == 0) {
                //成功删除文件
                file.delete();
                rapid = true;//成功
                try {
                    PreparedStatement ps = DbUtil.connection.prepareStatement("update sn_upload set state = 1, bd_upload_id = ? where id = ?");
                    ps.setString(1, uploadid);
                    ps.setInt(2, id);
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        if (!rapid) {
            //秒传失败或者小于262144，直接上传或者分片上传
            if (file.length() <= BaiduDiskUtil.FILE_CHUNK_SIZE) {
                //小于262144不分片
                jsonObject = BaiduDiskUtil.superFile2(remotePath, uploadid, 0, file);
                System.out.println("不分片上传返回：" + jsonObject);
                if (jsonObject.containsKey("partseq") && jsonObject.getInt("partseq").equals(0)) {
                    //成功删除文件
                    file.delete();
                    jsonObject = BaiduDiskUtil.createFile(remotePath, file.length(), uploadid, TARGET_PATH, String.format("[\"%s\"]", jsonObject.getStr("md5")));
                    System.out.println("创建文件返回" + jsonObject);
                    if (jsonObject.getInt("errno") == 0) {
                        try {
                            PreparedStatement ps = DbUtil.connection.prepareStatement("update sn_upload set state = 1, bd_upload_id = ? where id = ?");
                            ps.setString(1, uploadid);
                            ps.setInt(2, id);
                            ps.executeUpdate();
                        } catch (SQLException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } else {
                //分片
                long count = file.length() / BaiduDiskUtil.FILE_CHUNK_SIZE + (file.length() % BaiduDiskUtil.FILE_CHUNK_SIZE > 0 ? 1 : 0);
                try {
                    PreparedStatement ps = DbUtil.connection.prepareStatement("update sn_upload set split_count = ?, bd_upload_id = ? where id = ?");
                    ps.setLong(1, count);
                    ps.setString(2, uploadid);
                    ps.setInt(3, id);
                    ps.executeUpdate();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                NumberFormat nf = NumberFormat.getInstance();
                nf.setMinimumIntegerDigits(String.valueOf(count).length());
                nf.setGroupingUsed(false);
                boolean success = true;
                //List<String> remoteMd5s = new ArrayList<>(Convert.toInt(count));
                ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS,
                        new LinkedBlockingQueue<>(1000), new ThreadPoolExecutor.CallerRunsPolicy());
                for (int i = 0; i < count; i++) {
                    //文件分片存到数据库
                    long start = (long) BaiduDiskUtil.FILE_CHUNK_SIZE * i;
                    long end;
                    if (i == count - 1) end = file.length();
                    else end = (long) BaiduDiskUtil.FILE_CHUNK_SIZE * (i + 1);
                    String fileName = file.getName().substring(0, file.getName().lastIndexOf("."));
                    String filePath = path.substring(0, path.lastIndexOf("/"));
                    String outPath = String.format("%s/%s.%s.temp", filePath, fileName, nf.format(i + 1));
                    executor.execute(new SplitUploadThread(path, start, end, outPath, remotePath, uploadid, i, id));
                }
                AtomicBoolean uploadFinish = new AtomicBoolean(false);
                while (!uploadFinish.get()) {
                    if (executor.getCompletedTaskCount() == count) {
                        List<String> remoteMd5s = new ArrayList<>();
                        try {
                            PreparedStatement ps = DbUtil.connection.prepareStatement("select remote_md5 from sn_upload_split where upload_id = ? and state = 1 order by split_index asc");
                            ps.setInt(1, id);
                            ResultSet resultSet = ps.executeQuery();
                            while (resultSet.next()) {
                                remoteMd5s.add(resultSet.getString("remote_md5"));
                            }
                        } catch (SQLException e) {
                            e.printStackTrace();
                            //throw new RuntimeException(e);
                        }
                        // 线程池执行完
                        //cd /tmp/skyeysnow-netdisk/xxx/
                        JSONObject object = BaiduDiskUtil.createFile(remotePath, file.length(), uploadid, TARGET_PATH, JSONUtil.toJsonStr(remoteMd5s));
                        System.out.println("创建文件返回" + object);
                        //更新文件状态，删除源文件
                        if (object.getInt("errno") == 0) {
                            file.delete();
                            try {
                                DbUtil.statement.executeUpdate("update sn_upload set state = 1 where id = " + id);
                            } catch (SQLException e) {
                                e.printStackTrace();
                            }
                        }
                        uploadFinish.set(true);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            //throw new RuntimeException(e);
                        }
                    }
                }
            }
        }
    }
}
